\documentclass{cours}

\title{\textsc{Techniques de développement}}
\author{Notes prises d'après le cours de M. Gabriel Frey}
\date{Université de Strabourg}

\begin{document}

\chapter{Caractéristiques des langages de programmation}
\minitoc

\section{Niveaux de langage}
La notion de profondeur désigne la distance du langage par rapport au travail de
la machine. Le langage de haut niveau a un plus haut niveau d'abstraction que
les langages machines. Là où les langages de bas niveau s'occupent des registres, des accès mémoires
et des piles, les langages de haut niveau s'occupent de concepts plus élaborés
tels que les processus légers, verrous, objets, variables, tableaux,
arithmétique complexe et expressions booléennes. Les langages de haut niveau
n'ont pas la possibilité de s'occuper des détails liés à la machine tels que la
gestion mémoire contrairement aux langages de bas niveau. D'autres
caractéristiques telles que des routines de manipulation de chaîne de
caractères, les concepts des langages objets peuvent être présentes.

Avec un langage de haut niveau, les calculs sont décrit de manière plus
abstraite. La programmation est plus facile, et le programmeur n'a pas besoin de
gérer tout les détails comme l'allocation mémoire. En revanche, il maîtrise
moins les détails.

En revanche, avec un langage de bas niveau, les calculs sont décris dans un
langage proche de l'exécution réelle du microprocesseur. Le programmeur a une
maîtrise plus grande des détails, mais la programmation est plus difficile.

\section{Paradigme}
Un paradigme de programmation fournit (et détermine) la vue qu’a le développeur
de l’exécution de son programme. Par exemple, en programmation orientée objet,
les développeurs peuvent considérer le programme comme une collection d’objets
en interaction, tandis qu’en programmation fonctionnelle un programme peut être
vu comme une suite d'évaluations de fonctions sans états. Lors de la
programmation d’ordinateurs ou de systèmes multi-processeurs, la programmation
orientée processus permet aux développeurs de voir les applications comme des
ensembles de processus agissant sur des structures de données localement
partagées.\\

\begin{itemize}
\item{programmation impérative (Basic, Pascal, C...),}
\item{programmation structurée,}
\item{programmation fonctionnelle (Lisp, Scheme, OCaml, Erlang...),}
\item{programamtion orientée objet (Smalltalk, C++, Java...),}
\item{programmation déclarative (Prolog, Oz...),}
\item{programmation concurrente,}
\item{programamtion distribuée (Erlang...).}
\end{itemize}
\paragraph{}

Certains langages peuvent supporter des paradigmes multiples, comme le C++,
l'OCaml, le Ruby...

\section{Langage compilé et langage interpreté}
Un langage est dit compilé quand le programme est transformé en un programme en
code machine directement exécutable par le microprocesseur. On peut citer en
exemple C, C++ et OCaml.

Un langage interprété nécessite un programme, appelé interprète ou machine
virtuelle pour exécuter le programme. Cela rend le programme plus portable, mais
également plus lent. On peut citer en exemple Java, Ruby, Python ou PHP.

On peut également rencontrer des programmes compilés à la volée (just in time).
Certains langages permettent de compiler le programme sous forme de bytecode. Un
bytecode est un code intermédiaire de bas niveau mais qui n'est pas exécutable
directement par un microprocesseur.

\section{Typage}
Le typage est le fait d'attribuer un type aux éléments du code source
(variables, fonctions, etc.). Un type définit une structure de données : nombre
entier, tableau, chaîne de caractères. Dans certains langages, il existe des
types plus évolués (liste chaînée, sémaphore) et il est parfois possible d'en
définir de nouveaux.

Les types que l'on trouve dans un langage de programmation dépendent de sa
sémantique et donc de ses paradigmes. Beaucoup de langages proposent la notion
de variable qui associe un nom à une valeur en mémoire et ce nom ou cette valeur
à un type.

\subsection{Typage statique et typage dynamique}
Le typage est dit statique quand la vérification des types se fait dès la phase
de compilation. Cela permet d'utiliser l'inférence de type, un mécanisme qui
permet à un compilateur ou un interpréteur de rechercher automatiquement les
types associés à des expressions, sans qu'ils soient indiqués explicitement dans
le code source.

Avec un typage dynamique, la vérification des types se fait à l'exécution. Cela
comporte quelques inconvénients : il y a moins d'erreurs de type détectées et
une perte d'efficacité. En revanche, l'introspection du programme (sa capacité à
examiner son propore état) est facilitée.

\subsection{Typage explicite et typage implicite}
Si le typage est expicite, le programmeir est obligé d'indiquer lui-même les
types qu'il utilise, par exemple lors de déclaration de types ou de fonctions.
En revanche, avec un typage implicite, le programmeur laisse le soin au
compilateur ou l'interpréteur de déterminer tout seul les types de données
utilisés, par inférence par exemple.

\subsection{Typage fort et typage faible}
Un langage de programmation est dit fortement typé si la compilation ou
l'exécution peuvent détecter des erreurs de typage. Si ces erreurs ne sont
jamais reconnues, le langage est faiblement typé (c'est, par exemple, le cas de
PHP). Les conversions implicites de types sont formellement interdites. Si de telles
conversions sont possibles, le langage est faiblement typé.

\chapter{Compilation séparée}
\minitoc
\section{Étapes de la compilation}
Un compilateur fonctionne par analyse-synthèse : au lieu de remplacer chaque
construction du langage source par une suite équivalente de constructions du
langage cible, il commence par analyser le texte source pour en construire une
représentation intermédiaire qu'il traduit à son tour en langage cible.

\paragraph{}
Les étapes de la compilation incluent :

\subsection{Pré-traitement} 
Cette étape est indispensable en C. Cette étape prend
en charge la substitution de macro et de la compilation conditionnelle.

\subsection{Analyse lexicale} 
Cette étape découpe le code source en petits morceaux
appelés jetons (tokens). Chaque jeton est une unité atomique unique de la langue (unités
lexicales ou lexèmes), par exemple un mot-clé, un identifiant ou un symbole. La 
syntaxe de jeton est généralement un langage régulier, donc un automate à états 
finis construits sur une expression régulière peut être utilisé pour le reconnaître.

\subsection{Analyse syntaxique} 
Cette étape implique l'analyse de la séquence
jeton pour identifier la structure syntaxique du programme.
Cette phase s'appuie généralement sur la construction d'un arbre d'analyse ; on
remplace la séquence linéaire des jetons par une structure en arbre construite
selon la grammaire formelle qui définit la syntaxe du langage. L'arbre d'analyse
est souvent modifié et amélioré au fur et à mesure de la compilation

\subsection{Analyse sémantique} 
C'est la phase durant laquelle le compilateur ajoute des informations
sémantiques à l'arbre d'analyse et construit la table des symboles.
Cette phase vérifie le type (vérification des erreurs de type), ou l'objet de
liaison (associant variables et références de fonction avec leurs définitions),
ou une tâche définie (toutes les variables locales doivent être initialisées
avant utilisation), peut émettre des avertissements, ou rejeter des programmes
incorrects.

\subsection{Code intermédiaire}
À cette étape, le compilateur transforme le code source en code intermédiaire.
Ce code intermédiaire constitue une interface claire entre le langage et la
machine. Une fois ce code intermédiaire obtenu, on y applique des techniques
d'optimisation, afin de rendre le code "meilleur".

\subsection{Génération de code}
À partir du code intermédiaire optimisé, le compilateur génère du code avec
l'allocation de registres. Il traduit ensuite le code intermédiaire en code objet,
avec éventuellement l'insertion de données de débogage et d'analyse de
l'exécution. 

\subsection{Édition de lien}
Enfin, on construit l'exécutable à partir des différents fichiers objets.

\paragraph{}
L'analyse lexicale, syntaxique et sémantique, le passage par un langage
intermédiaire et l'optimisation forment la partie frontale de la compilation. La
génération de code et l'édition de liens constituent la partie finale.

\section{Exercice : calcul de l'aire de l'intersection de deux cercles}
On cherche à écrire un programme en C qui calcule l'aire de l'intersection de
deux cercles en utilisant la méthode du crible. On effectue un tirage de $N$
points à l'intérieur du rectangle encadrant les deux cercles. La proportion des
points tirés aléatoirement appartenant aux deux cercles permet de déterminer 
l’aire de leur intersection.

\begin{figure}[h]
\centering
\includegraphics[width=6cm]{exo_cercles.png}
\caption{Exemple de calcul d'aire avec $N=10$}
\end{figure}

%\paragraph{}
Afin d'écrire ce programme, indiquer :

\begin{itemize}
\item les types à définir,
\item les prototypes de fonction,
\item la structure du programme (appel de fonctions),
\item la répartition par fichier des types et des fonctions.
\end{itemize}

\subsection{Types}
\subsubsection{Type point:}
\begin{verbatim}
struct str_point{
    float x;
    float y;
};
typedef struct str_point point;
\end{verbatim}

\subsubsection{Type cercle:}
\begin{verbatim}
struct str_cercle{
    point centre;
    float rayon;
};
typedef struct str_cercle cercle;
\end{verbatim}

\subsubsection{Type rectangle:}
\begin{verbatim}
struct str_rectangle{
    point x_min_y_min;
    point x_max_y_max:
};
typedef struct str_rectangle rectangle;
\end{verbatim}

\subsection{Fonctions}
\begin{verbatim}
rectangle rectangle_encadrant(cercle c1, cercle c2);
float xmin(cercle c1, cercle c2);
float ymin(cercle c1, cercle c2);
float xmax(cercle c1, cercle c2);
float ymax(cercle c1, cercle c2);
int compareX(cercle c1, cercle c2);
int compareY(cercle c1, cercle c2);
point point_aleatoire(rectangle rect);
float aleatoire(float min, float max);
int appartient(point pt, cercle cerc);
float distance(point pt1, point pt2);
float surface_rectangle(rectangle rect);
\end{verbatim}

\subsection{Algorithme}
\begin{verbatim}
//Initialisation

cercle c1;
cercle c2;
rectangle rect = rectangle_encadrant(c1, c2);
int nbr_point_intersection = 0;

//boucle tirage point aléatoire

Répéter N fois {
    point pt_aleat = point_aleatoire(rect);
    if(appartient(pt_aleat, c1) && appartient(pt_aleat, c2))
    {nbr_point_intersection++;}
}

proportion_point_intersection = nbr_point_intersection / N;

surface_intersection = 
    proportion_point_intersection * surface_rectangle(rect);
\end{verbatim}

\subsection{Compilation du programme}
\subsubsection{Première version:}
Une façon naïve d'écrire ce programme serait de regrouper tous les types, tous
les prototypes et définitions de fonctions dans un seul et unique fichier
\texttt{main.c}. La compilation se ferait alors à l'aide de la commande :
\begin{verbatim}
gcc -o MAIN main.c
\end{verbatim}

\subsubsection{Deuxième version:}
Une façon plus intelligente d'écrire ce programme serait de regrouper les types
et les prototypes de fonctions dans un fichier \texttt{geom.h}. Les définition des
fonctions quant à elles seront regroupées dans un fichier \texttt{geom.c}. Le
fichier \texttt{geom.h} sera inclus dans \texttt{main.c} et \texttt{geom.c} en utilisant
la commande préprocesseur \texttt{include} \texttt{"geom.h"}.

Pour compiler le programme avec cette configuration, on crée d'abord les
fichiers objets avec les commande suivantes :
\begin{verbatim}
gcc -c main.c
gcc -c geom.c
\end{verbatim}

On crée ensuite l'exécutable à l'aide de la commande :
\begin{verbatim}
gcc -o MAIN maic.o geom.o
\end{verbatim}

\subsubsection{Troisième version (version finale):}
La façon la plus efficace d'écrire ce programme consiste à séparer les
définitions de type et les prototypes de fonctions dans des fichiers header 
(\texttt{.h}) séparés. Cette configuration permet de recompiler uniquement les
fichiers modifés. Elle apporte également une clarté et une lisibilité grâce aux
types et prototypes clairement identifiables dans les headers. Pour éviter les
définitions multiples de types ou de fonctions, il est nécessaire de faire
figurer, dans l'en-tête du fichier \texttt{header.h} les commandes préprocesseur
suivantes :
\begin{verbatim}
#ifndef __HEADER_H_
#define __HEADER_H_
//contenu de header.h
#endif
\end{verbatim}

\begin{figure}[h]
\centering
\includegraphics[width=13cm]{compil_separee.png}
\caption{Schéma de compilation du programme en version 2}
\end{figure}

\subsection{Automatisation de la compilation}
Pour compiler le programme avec cette configuration, il est nécessaire de
générer chaque fichier objet à partir de chaque fichier \texttt{.c}, et seulement
là de créer l'exécutable. Pour notre exemple, il faudrait rentrer les commandes
suivantes :
\begin{verbatim}
gcc -c point.c
gcc -c cercle.c
gcc -c rectangle.c
gcc -c main.c
gcc -o MAIN main.o rectangle.o cercle.o point.o
\end{verbatim}

Cette opération est assez fastidieuse. Pour automatiser la compilation, nous
allons utiliser le logiciel \texttt{make}. \texttt{make} utilise un fichier de configuration
appelé \texttt{makefile}. Ce dernier décrit des cibles (qui sont souvent des fichiers, mais
pas toujours), de quelles autres cibles elles dépendent, et par quelles actions
(des commandes) y parvenir. Le schéma d'un makefil est le suivant :
\begin{verbatim}
cible : dépendances
    commande
\end{verbatim}

\textbf{Attention:} la tabulation avant la commande est obligatoire.

Pour compiler notre exemple, nous allons utiliser le \texttt{makefile} suivant :
\begin{verbatim}
main : point.o cercle.o rectangle.o main.o
    gcc –o main main.o rectangle.o cercle.o point.o
point.o : point.c point.h
    gcc –c point.c
cercle.o : cercle.c cercle.h point.h
    gcc –c cercle.c
rectangle.o : rectangle.c rectangle.h cercle.h point.h
    gcc –c rectangle.c
main.o : main.c
    gcc –c main.c
\end{verbatim}

Pour compiler le programme, il suffira d'utiliser la commande \texttt{make}.

\chapter{Variables}
\minitoc
\section{Portée des variables}
La portée d'une variable est l'étendue au sein de laquelle la variable est liée.
Ce chapitre distingue les différents types de portée et de persistence des
variables.


\subsection{Variables locales et globales}
Une variable dite locale est une variable qui ne peut être utilisée que dans le
bloc ou elle a été définie. \textit{A contrario}, une variable globale peut être
utilisée dans la totalité du programme. L'usage de variables globales peut
apporter quelques problème. Une variable globale peut être modifiée par une
autre fonction (on appelle cela l'effet de bord). Cette modification peut être
voulue, mais également arriver par erreur. Les variables globales peuvent
également amener à des définitions multiples de symboles. Cette erreur est
détectée à l'édition de lien.

\subsection{Masquage de variable}
Si plusieurs variable portent le même nom, les variables les plus externes sont
masquées dans les sous blocs.

\subsection{Durée de vie d'une variable}
Il y a deux durées de vie possible pour une variable :

\paragraph{Durée de vie permanente:} l’emplacement mémoire utilisé par la
variable est alloué lors de la compilation du programme
et demeure inchangé durant son exécution. Les variables 
permanentes sont initialisées à zéro.
\paragraph{Durée de vie temporaire:} l’emplacement mémoire de la variable est
alloué lors de l’appel de la fonction dans laquelle elle est
définie, et est libéré lors du retour de cette fonction.

\paragraph{}De ces définitions découlent plusieurs règles :

\begin{itemize}
\item les variables globales sont toujours permanentes,
\item les paramètres de fonctions sont toujours des variables temporaires,
\item les variables définies dans un bloc sont généralement des variables
temporaires,
\item l'espace mémoire des variables locales est alloué à chaque entrée dans une
fonction et libéré à la sortie de la fonction.
\end{itemize}

\paragraph{}Il est néanmoins possible de rendre la durée de vie d'une variable de bloc
permanente en utilisant le mot-clé \texttt{static}.

\section{Variable statique}
Une variable statique à une durée de vie permanente. Elles ne sont initialisées
qu'une seule fois et conservent leur valeur d'un appel de fonction à l'autre. On
définit les variables globales de la façon suivante :
\begin{verbatim}
static <définition variable>;
\end{verbatim}

\subsection{Variable globale statique}
Il n'est pas possible d'avoir plusieurs variables globales portant le même nom 
dans un programme : cela provoquerait une erreur à la création de lien. Il est
cependant possible de limiter la portée d'une variable globale avec le mot-clé
\texttt{static} : la portée sera limitée au seul fichier contenant a définition.
 
\subsection{Variable globale externe}
Dans un programme, un fichier peut avoir à utiliser une variable globale d'un
autre fichier. Cette variable doit être déclarée avec le mot-clé \texttt{extern}
pour précisée qu'elle a été définie ailleurs.

L'utilisation d'une variable globale est délicate. Il faut éviter de les
utiliser au maximum. Si cette variable est utile dans un seul fichier, il faut
la déclarer statique, ce qui limite sa portée au fichier. Si elle est utile dans
plusieurs fichiers, on la déclare une première fois classiquement et les autres
fois externe, ce qui informe au compilateur qu'elle est définie dans un autre
fichier. À la déclaration (classique ou statique), il faut intialiser cette
variable (bien qu'elle soit par défaut initialisée à 0). On n'initialise pas la
variable si elle est déclarée en externe. Dans tous les cas, il est important de
compiler avec l'option \texttt{-Wall}.

\section{Portée des fonctions}
Toute fonction peut être définie et utilisée dans n'importe quelle partie du
programme.
\subsection{Fonction statique}
Il est possible de limiter la portée d'une fonction à un fichier en la déclarant
statique. Les fonctions statiques réalisent généralement un traitement de plus
haut niveau.

\subsection{Fonction externe}
Pour utiliser correctement une fonction d’un autre fichier, il est nécessaire 
de connaître le profil (prototype) de la fonction.
Pour utiliser une fonction d’un autre fichier, le prototype de la fonction doit 
être précédé du mot clé \texttt{extern}.
Si le profil n’est pas connu, le compilateur considère par
défaut que les paramètres et la valeur de retour sont de
type int. Des opérations de conversions de type sont
insérées dans le programme.

\section{Autres qualificatifs des variables}
\subsection{Variable automatique et registre}
En C, les variables temporaires sont par défaut automatiques. On peut néanmoins
rendre cette déclaration explicite en utilisant le mot-clé \texttt{auto}. Une 
variable automatique est sauvegardée dans la pile d'exécution. Les variables
globales ne peuvent pas être automatiques. Une variable registre, elle, est
rangée dans un registre. On les déclare en utilisant le mot-clé \texttt{register}.

Les variables automatiques et registres sont finalement des indications pour le
compilateur. Il est en revanche préférable d'utiliser les options d'optimisation
du compilateur.

\subsection{Variable volatile}
Un variable volatile signale au compilateur que la variable est susceptible
d'être modifiée indépendamment du déroulement du programme. On peut citer par
exemple une variable modifiée sur réception d'un signal, ou une variable
utilisant une zone mémoire utilisée par le hardware. On les déclare en utilisant
le mot-clé \texttt{volatile}.

%\chapter{Pointeurs}
%\minitoc
%\chapter{Bibliothèques standard}
%\minitoc
%\chapter{Automatisation de tâches}
%\minitoc

\chapter{Outils pour la programmation en C}
\minitoc
Il existe différents outils permettant d'analyser le comportement d'un
programme: les debbugers (comme \texttt{gdb}), les verificateurs d'accès mémoire
(comme \texttt{valgrind}) et les analyseurs de performances (les profiler). Ce
chapitre à pour ambition de donner un apercu des outils les plus connus.

\section{Debugger}
Un debugger est une logiciel analysant les bugs d'un programme. Pour cela, il
permet d'exécuter le programme pas-à-pas, d'afficher la valeur des variables à
tout moment, de mettre en place des points d'arrêt sur des conditions ou sur des
lignes du programme ...

\begin{figure}[h]
\centering
\includegraphics[width=6cm]{gdb.png}
\caption{Positionnement du debugger dans le processus de développement}
\end{figure}

\subsection{GNU Debugger : gdb}
Le metteur au point utilise les informations ajoutées à l’exécutable lors de la
compilation avec l’option \texttt{-g} pour s’intercaler entre l’utilisateur et
l’exécutable.
Il permet à l’utilisateur de guider l’exécution (lignes de codes exécutées une
par une, saut ou lecture de fonctions, ...) et d’afficher des informations sur
l’état du programme (variables, état de la pile, ...) qu’il peut modifier à la
demande. On peut également utiliser gdb directement dans un terminal.
\begin{verbatim}
$ gdb <nom_programme>
\end{verbatim}

\subsection{Utilisation de gdb}
Il y a plusieurs modes d'utilsation de \texttt{gdb}, en ligne de commande ou avec
une interface graphique. Certains IDE intègrent \texttt{gdb} directement.

Lors de l'utilisation, \texttt{gdb} se stoppe lors de conditions spécifiées. Il
permet ensuite d'examiner ce qui s'est passé quand le programme s'est arrêté
(erreur de segmentation par exemple). On peut ensuite corriger l'éventuel bug, à
la volée ou en recompilant ensuite le programme.

\subsection{Commandes de gdb}
Toutes les commandes suivantes peuvent être appelées à chaque interruption de
l'exécution.

\paragraph{Exécution}
\begin{itemize}
\item{\texttt{run} \texttt{<liste arguments>} (ou \texttt{r}): lance l’exécution le 
programme (avec la liste arguments),}
\item{\texttt{kill} : tue le programme en cours de mise au point,}
\item{\texttt{quit} : quitte le debugger,}
\item{\texttt{help} \texttt{<sujet>} : documentation en ligne.}
\end{itemize}

\paragraph{Affichage du code}
\begin{itemize}
\item{\texttt{list} \texttt{[<nom fonction>|<num ligne>]} : affiche 10 lignes du code source
centrées sur la fonction, ou sur ligne num, ou autour de la ligne
courante (si pas d’arguments).}
\end{itemize}

\paragraph{Arrêt de l'exécution}
\begin{itemize}
\item{\texttt{break} \texttt{[fichier:] [<nom fonction>|<num ligne>]} (ou \texttt{b}): placer 
un point d’arrêt,}
\item{\texttt{watch} \texttt{<variable>} : la variable doit être définie dans le
contexte courant,}
\item{\texttt{info} \texttt{breakpoints}: liste des points d’arrêts,}
\item{\texttt{disable/enable} \texttt{breakpoints} \texttt{<num} \texttt{breakpoint>}: desactive/reactive le
breakpoint,}
\item{\texttt{clear} \texttt{[<nom fonction>|<num ligne>]}, \texttt{delete}
\texttt{<num} \texttt{breakpoint>}: supprime le  breakpoint.}
\end{itemize}

\paragraph{Guidage interactif de l'exécution après un arrêt}
\begin{itemize}
\item{\texttt{continue} (ou \texttt{c}) : continuer l’exécution du programme jusqu’au prochain
point d’arrêt,}
\item{\texttt{step} \texttt{<num>} (ou \texttt{s}) : passer à la ligne suivante (ou au <num> lignes
suivantes), en entrant dans les sous-fonctions,}
\item{\texttt{next} \texttt{<num>} (ou \texttt{n}): passer à l’instruction suivante (ou au <num> lignes
suivantes), en n’entrant pas dans les sous-fonctions (un appel de
fonction est considéré comme une instruction atomique),}
\item{\texttt{finish}: termine l’appel de la fonction courante.}
\end{itemize}

\paragraph{Inspection de l'environnement}
\begin{itemize}
\item{\texttt{backtrace} (ou \texttt{bt}): affiche la pile d’appels,}
\item{\texttt{where}: contexte courant,}
\item{\texttt{print} \texttt{[<variable>|<expression>|<variable>=<valeur>]} (ou
\texttt{p}): Affiche la valeur de la variable / l’expression, calculée d’après 
le contexte courant. Permet aussi de modifier la valeur affectée à une variable,}
\item{\texttt{x} \texttt{variable}:affiche l'adresse d'une variable,}
\item{\texttt{display} \texttt{<expression>}: affiche le résultat de l’évaluation de l’expression
à chaque arrêt,}
\item{\texttt{undisplay} \texttt{<expression>}: annule l’affichage de l’expression à chaque
arrêt.}
\end{itemize}

\section{Analyse de la mémoire}
Les analyseurs de mémoire permettent de détecter les erreurs d'allocation, de
désallocation et d'accès mémoire. Ce sont des bugs courants, souvent difficiles à
détecter, et provoquant des résultats incorrects ou des interruptions
d’exécution. Parmi ces bugs, on compte les pointeurs fous (\emph{dandling
pointer}, \texttt{segmentation} \texttt{fault}), causés par des accès non autorisés en
mémoire, ou les fuites de mémoire (\emph{memory leak}, \texttt{not} \texttt{enough}
\texttt{memory}).

Il existe différents outils d'analyse qui travaillent de façon différente. Ici,
nous étudierons plus précisement Valgrind. Valgrind vérifie les accès mémoire à
l'exécution. Il fait ensuite un bilan des \texttt{malloc} et des \texttt{free} (pour 
cela, valgrind encapsule dans des fonctions qui lui sont propre les appels à 
\texttt{malloc} et à \texttt{free}).

Il faut préciser l'outil de valgrind à utiliser sur le programme à corriger.
Le programme à inspecter doit avoir été compilé avec les options de debuggage
(\texttt{-g}).
Valgrind vérifie uniquement les zones mémoires allouées dynamiquement (pas les 
zones statiques).

\subsection{Outils de Valgrind}
\paragraph{memcheck}
Vérification de la correction des accès mémoires, accès à des
variables non-initialisées, mauvaise utilisation de free, détection de fuite 
mémoires... (outil par défaut).

\paragraph{ptrcheck}
Outil similaire à memcheck, mais encore à un état expérimental. Il peut détecter
des erreurs que memcheck ne détecte pas.

\paragraph{cachegrind, callgrind}
Gestion des accès au cache processeur. Cet outil permet d'optimiser le
programme.

\paragraph{hellgrind, drd}
Pour les programmes multi-threadés. Permet de gérer les accès concurrents.

\paragraph{massif}
Outils de gestion du tas. Il permet de diminuer la quantité de mémoire utilisée
par le programme.

\section{Documentation}
Il est impératif de commenter le code de façon claire. Il est également possible
de générer automatiquement de la documentation à partir des fichiers source
commentés : \texttt{javadoc} pour le java, \texttt{doxygen} pour le C...

Pour utiliser \texttt{doxygen}, il faut ajouter des commentaires qu'il puisse
utiliser. Ces commentaires sont de la forme \texttt{/**} \texttt{/}, ou encore
\texttt{/!} \texttt{*/}. Certaines informations particulières peuvent être marquées
par des balises \texttt{@}. Par exemple :

\begin{itemize}
\item{Description d'un module (au début d'un fichier)}
\begin{verbatim}
/**
* @file fichier.c
* @brief description
* @author nom auteur
* @date date
*/
\end{verbatim}
\item{Description d'une fonction}
\begin{verbatim}
/** @brief breve description de la fonction
* @param <nom du paramètre> <description du paramètre>
* @return <description du retour de la fonction
*/
\end{verbatim}
\end{itemize}

\section{Gestion de versions}
\subsection{Pourquoi plusieurs versions d'un fichier ?}
Conserver plusieurs versions d'un fichier permet de revenir facilement en
arrière, à une version stable et faire des corrections. Cela permet également de
conserver un historique de toutes les opérations. Dans un travail en équipe, il
est indispensable de conserver plusieurs versions. On peut travailler en
parallèle sur plusieurs branches. La gestion des versions garantit la sécurité
(intégrité, disponibilité, confidentialité) des données. On peut s'en servir
pour du code, mais aussi pour un site web ou de la documentation.

Pour gérer plusieurs versions d'un fichier, on peut le faire manuellement
bien sûr, mais on peut également utiliser un logiciel dédié.

\subsection{Concepts de base}
\paragraph{Diff:} c'est un utilitaire linux qui compare deux fichiers et produit 
un fichier \texttt{patch} contenant la différence entre deux fichiers (lignes ajoutées 
ou supprimées).

\paragraph{Patch:} c'est un utilitaire linux qui applique un patch à un fichier 
et génère un nouveau fichier.

Dans certains cas, il est plus intéressant de sauvegarder les modifications d'un
fichier que $n$ copies d'un fichier.

\subsection{Vocabulaire}
\paragraph{Dépôt (\texttt{Repository}):} C'est un répertoire partagé par tous, 
qui conserve l'historique de toutes les modifications.

\paragraph{Révision:}chaque fichier à un numéro de version unique. Une
incompatibilité d'interfaces doit entraîner un changement majeur. Les
mineurs correspondent plutôt à des évolutions compatibles.

\paragraph{Branches:}elles permettent de corriger un problème sur une
ancienne vesion. Cela permet également de développer deux idées en
parallèle, de gérer sa propre version d'un logiciel ou de fusionner
après une divergence.

\paragraph{Tags:}ce sont des marques symboliques sur un branche. Ils
permettent de définir des versions, ou encore de nommer des branche
(alpha, beta...).

\subsection{Travail à plusieurs}
Il n'y a pas de verrou sur les sources : chaque utilisateur à sa propore
copie de travail. La gestion des conflits se déroule de la façon suivante :
\begin{itemize}
\item{pas de \texttt{commit} sans \texttt{update} préalable. Cette
manipulation permet d'éviter un maximum de conflits.}
\item{si il n'y a pas de conflits détectés, on fusionne automatiquement
les branches}
\item{si des conflits sont détectés, les développeurs doivent les
résoudre manuellement}
\item{on ne peux pas faire de nouveau \texttt{commit} avant la
résolution du conflit}
\end{itemize}

\subsubsection{Autres fonctionnalités}
On peut également visualiser l'histoirque des versions sous diverses
formes, annoter le code avec les contributions de chaque développeur ou
encore rajouter une interface graphique (en utilisant par exemple
RedMine).


\end{document}
